/**
 * Copyright (c) 2008, Daniel Gee. All Rights reserved.
 * See LICENSE.txt for more details.
 */
package com.rl.dungeon.fov;

/**
 * A single View instance represents an arc from 0,0 into the first quadrant. The line is bounded on
 * each of two sides by a {@link SightLine}. For a fresh View the "shallow line" goes from (0,1) out
 * to (infinity,0), and the "steep line" goes from (1,0) to (0,infinity). The trick is to update
 * theses lines as walls get in the way. Walls can be above or below both lines, between the lines,
 * on one line, or on both lines.<br />
 * <br />
 * Wall squares (that is, squares that block sight) affect the View as follows:
 * <ul>
 * <li>If the wall is above or below both lines, it doesn't affect this view.</li>
 * <li>If the wall is between the lines then two smaller views need to be created, one to each side
 * of the wall.</li>
 * <li>If the wall is on only one line, then the line needs to be bumped and the view adjusted.</li>
 * <li>If the wall is on both lines, then the view is completely blocked and needs to be disposed
 * of.</li>
 * </ul>
 * Clear space has only a single rule:
 * <ul>
 * <li>If the clear space is on either or both lines, or between the lines, then it's visible to
 * this view.</li>
 * </ul>
 * 
 * @author Daniel Gee
 */
class View implements Cloneable
{
	/**
	 * The most recent shallow bump for this view.
	 */
	public ViewBump shallowBump;

	/**
	 * {@link SightLine} that marks the "rightmost" limit of this View. Edits to this view will
	 * modify this line instead of making new ones.
	 */
	public final SightLine shallowLine;

	/**
	 * The most recent steep bump for this view.
	 */
	public ViewBump steepBump;

	/**
	 * {@link SightLine} that marks the "leftmost" limit of this view. Edits to this view will
	 * modify this line instead of making new ones.
	 */
	public final SightLine steepLine;

	/**
	 * Makes a new view with no bumps.
	 * 
	 * @param shallowLine
	 *            the shallow line of the view.
	 * @param steepLine
	 *            the steep line of the view.
	 */
	public View(SightLine shallowLine, SightLine steepLine)
	{
		super();
		this.shallowLine = shallowLine;
		this.steepLine = steepLine;
		this.shallowBump = null;
		this.steepBump = null;
	}

	/**
	 * Private constructor that allows for specifying all four member variables. Used during
	 * cloning.
	 * 
	 * @param shallowLine
	 *            shallow line of the view.
	 * @param steepLine
	 *            steep line of the view.
	 * @param shallowBump
	 *            shallow bump of the view.
	 * @param steepBump
	 *            steep bump of the view.
	 */
	private View(SightLine shallowLine, SightLine steepLine, ViewBump shallowBump,
			ViewBump steepBump)
	{
		this.shallowBump = shallowBump;
		this.shallowLine = shallowLine;
		this.steepBump = steepBump;
		this.steepLine = steepLine;
	}

	/**
	 * A cloned View also performs a deep copy of all associated {@link SightLine} and
	 * {@link ViewBump} instances.
	 */
	@Override
	public View clone()
	{
		/**
		 * Since I know that this class implements Clonable I'm not gonna call super.clone() even
		 * though that's part of the contract because I'm at the top of the class tree except for
		 * Object, and Object's clone doesn't do anything except maybe throw an exception. So I'm
		 * just gonna make a new View instance and copy data from here into that.
		 */
		return new View(this.shallowLine.clone(), this.steepLine.clone(),
				(this.shallowBump == null ? null : this.shallowBump.clone()),
				(this.steepBump == null ? null : this.steepBump.clone()));
	}

	/**
	 * Views are equal if they have the same Lines and ViewBumps.
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
		{
			return true;
		}
		if (obj == null)
		{
			return false;
		}
		if (this.getClass() != obj.getClass())
		{
			return false;
		}
		final View other = (View) obj;
		if (this.shallowBump == null)
		{
			if (other.shallowBump != null)
			{
				return false;
			}
		}
		else if (!this.shallowBump.equals(other.shallowBump))
		{
			return false;
		}
		if (this.shallowLine == null)
		{
			if (other.shallowLine != null)
			{
				return false;
			}
		}
		else if (!this.shallowLine.equals(other.shallowLine))
		{
			return false;
		}
		if (this.steepBump == null)
		{
			if (other.steepBump != null)
			{
				return false;
			}
		}
		else if (!this.steepBump.equals(other.steepBump))
		{
			return false;
		}
		if (this.steepLine == null)
		{
			if (other.steepLine != null)
			{
				return false;
			}
		}
		else if (!this.steepLine.equals(other.steepLine))
		{
			return false;
		}
		return true;
	}

	/**
	 * Hashes based on Lines and ViewBumps.
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode()
	{
		final int PRIME = 31;
		int result = 1;
		result = PRIME * result + ((this.shallowBump == null) ? 0 : this.shallowBump.hashCode());
		result = PRIME * result + ((this.shallowLine == null) ? 0 : this.shallowLine.hashCode());
		result = PRIME * result + ((this.steepBump == null) ? 0 : this.steepBump.hashCode());
		result = PRIME * result + ((this.steepLine == null) ? 0 : this.steepLine.hashCode());
		return result;
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return "View(...)";
	}
}
